#include "stdafx.h"
#include <MyControls/LayerRow.h>
#include <MyControls/Xline.h>
#include <MyControls/App.h>

#include <cmath>

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

namespace My {

// Control

LayerControl::Control::Control(LayerControl & c) 
	: m_LayerControl(c), m_Timeline(c.m_Timeline)	
{
	m_LayerControl.Add(this);
}
	
void LayerControl::Control::HandleLeftDown(unsigned int flags, const CPoint & p) 
{
	if (!m_LayerControl.IsEmpty())
		m_LayerControl.GetLayer()->Select(true);
	My::Control::HandleLeftDown(flags, p);
}

bool LayerControl::Control::Visible() const
{
	return !m_LayerControl.IsEmpty();
}

// LeftBracket

CRect LayerControl::LeftBracket::Bounds() const
{
	return AlignRect(
		m_LayerControl.m_LeftGlyph.Bounds(),
		CPoint(m_Timeline.TimeToX(m_LayerControl.GetAbsoluteTrimming().first), 1),
		ALIGN_HRIGHT);
}

bool LayerControl::LeftBracket::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (m_LayerControl.IsEmpty())
		return false;
	m_StartPos = ClientToParent(p0).x;
	m_StartValue = m_Timeline.TimeToX(m_LayerControl.GetTrimming().first);
	return true;
}

void LayerControl::LeftBracket::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_LayerControl.UpdateTrimming();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void LayerControl::LeftBracket::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + ClientToParent(p).x - m_StartPos;
	int t = round(m_Timeline.XToTime(x));

	Span s = m_LayerControl.GetTrimming();
	if (t > s.last)
		t = s.last;
	s.first = t;
	m_LayerControl.SetTrimming(s);
	m_LayerControl.Update();

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	if (x < m_Timeline.GetGap())
		m_Timeline.SetTimeAutoScroll(x - m_Timeline.GetGap());
	else if (x > m_LayerControl.Width() - m_Timeline.GetGap())
		m_Timeline.SetTimeAutoScroll(x - m_LayerControl.Width() + m_Timeline.GetGap());
	else
		m_Timeline.SetTimeAutoScroll(0);
}

// RightBracket

CRect LayerControl::RightBracket::Bounds() const
{
	return AlignRect(
		m_LayerControl.m_RightGlyph.Bounds(),
		CPoint(m_Timeline.TimeToX(m_LayerControl.GetAbsoluteTrimming().last), 1),
		ALIGN_HLEFT);
}

bool LayerControl::RightBracket::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (m_LayerControl.IsEmpty())
		return false;
	m_StartPos = ClientToParent(p0).x;
	m_StartValue = m_Timeline.TimeToX(m_LayerControl.GetTrimming().last);
	return true;
}

void LayerControl::RightBracket::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	m_LayerControl.UpdateTrimming();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void LayerControl::RightBracket::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + ClientToParent(p).x - m_StartPos;
	int t = round(m_Timeline.XToTime(x));

	Span s = m_LayerControl.GetTrimming();
	if (t < s.first)
		t = s.first;
	s.last = t;
	m_LayerControl.SetTrimming(s);
	m_LayerControl.Update();

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	int gap = m_Timeline.GetGap();
	if (x < gap)
		m_Timeline.SetTimeAutoScroll(x - gap);
	else if (x > m_LayerControl.Width() - gap)
		m_Timeline.SetTimeAutoScroll(x - m_LayerControl.Width() + gap);
	else
		m_Timeline.SetTimeAutoScroll(0);
}

// LayerControl

LayerControl::LayerControl(LayerRow & row) : 
	m_LayerRow(row),
	m_pLayer(0),
	m_Timeline(row.m_Xline),
	m_LeftBracket(* this),
	m_RightBracket(* this)
{
	m_SourceOffset = -1;
	m_Trimming = Span(-1, -1);

	m_LeftGlyph.Load("LeftBracket", "LeftBracketMask");
	m_RightGlyph.Load("RightBracket", "RightBracketMask");
	VERIFY (m_LightGreenRipple.LoadBitmap("LightGreenRipple"));
	VERIFY (m_DarkGreenRipple.LoadBitmap("DarkGreenRipple"));
}

LayerControl::~LayerControl()
{
	SetLayer(0);
}

bool LayerControl::IsEmpty() const
{
	return GetLayer() == 0 || GetLayer()->IsEmpty();
}

int LayerControl::GetSourceOffset() const
{
	return m_SourceOffset;
}

Span LayerControl::GetSourceSpan() const
{
	if (IsEmpty())
		return Span(0, 0);
	else
		return Span(0, GetLayer()->GetSourceDuration()).Offset(GetSourceOffset());
}

void LayerControl::SetSourceOffset(int time)
{
	if (IsEmpty())
		return;
	Span s = m_Timeline.GetSequence()->GetTimeSpan();
	Span t = GetTrimming();
	s.first -= t.last;
	s.last -= t.first;
	if (time < s.first)
		time = s.first;
	if (time > s.last)
		time = s.last;
	if (m_SourceOffset != time) {
		m_SourceOffset = time;
		Invalidate();
	}
}

void LayerControl::UpdateSourceOffset()
{
	if (!IsEmpty())
		GetLayer()->SetSourceOffset(GetSourceOffset());
}

Span LayerControl::GetTrimming() const
{
	return m_Trimming;
}

Span LayerControl::GetAbsoluteTrimming() const
{
	return GetTrimming().Offset(m_SourceOffset);
}

void LayerControl::SetTrimming(Span s)
{
	if (IsEmpty())
		return;
	if (s.last < s.first)
		s.last = s.first;
	s.Intersect(Span(0, GetLayer()->GetSourceDuration()));
	if (s.first > int(m_Timeline.GetSequence()->GetDuration()) - GetSourceOffset())
		s.first = int(m_Timeline.GetSequence()->GetDuration()) - GetSourceOffset();
	if (s.last < - GetSourceOffset())
		s.last = - GetSourceOffset();
	if (m_Trimming != s) {
		m_Trimming = s;
		Invalidate();
	}
}

void LayerControl::UpdateTrimming()
{
	if (!IsEmpty())
		GetLayer()->SetTrimming(GetTrimming());
}

CRect LayerControl::Bounds() const
{
	if (m_LayerRow.GetList() == 0)
		return CRect(0, 0, 0, 0);
	CRect r = m_LayerRow.ClientRect();
	Span s = m_LayerRow.m_Xline.GetTimeCol().GetBounds();
	r.left = s.first;
	r.right = s.last;
	return r;
}

void LayerControl::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	if (!IsEmpty()) {
		Span s = GetLayer()->GetSourceSpan();
		int xmin = m_Timeline.TimeToX(s.first);
		int xmax = m_Timeline.TimeToX(s.last);
		GetLayer()->Select(xmin <= p.x && p.x <= xmax);
	}
	My::Control::HandleLeftDown(flags, p);
}

bool LayerControl::HandleDragStart(unsigned int flags, const CPoint & p0, const CPoint & p)
{
	if (IsEmpty())
		return false;
	Span s = GetLayer()->GetSourceSpan();
	if (m_Timeline.TimeToX(s.first) <= p0.x && p0.x <= m_Timeline.TimeToX(s.last)) {
		m_StartPos = p0.x;
		m_StartValue = m_Timeline.TimeToX(GetSourceOffset());
		s = GetLayer()->GetSpan();
		m_DragTrimming = !(m_Timeline.TimeToX(s.first) <= p0.x && p0.x <= m_Timeline.TimeToX(s.last));
		m_StartTrimming = GetTrimming().Offset(GetSourceOffset());
		return true;
	} else
		return false;
}

void LayerControl::HandleDragEnd(unsigned int flags, const CPoint & p)
{
	UpdateSourceOffset();
	if (m_DragTrimming)
		UpdateTrimming();
	m_Timeline.OnAutoScroll(false);
	m_Timeline.SetTimeAutoScroll(0);
}

void LayerControl::HandleDragging(unsigned int flags, const CPoint & p)
{
	int x = m_StartValue + p.x - m_StartPos;
	int t = round(m_Timeline.XToTime(x));
	if (m_DragTrimming) {
		if (t > m_StartTrimming.first)
			t = m_StartTrimming.first;
		int d = GetLayer()->GetSourceDuration();
		if (t < m_StartTrimming.last - d)
			t = m_StartTrimming.last - d;
		Span s = m_StartTrimming;
		SetTrimming(s.Offset(- t));
	}
	SetSourceOffset(t);

	// Autoscroll?
	x -= m_StartValue - m_StartPos;
	if (x < m_Timeline.GetGap())
		m_Timeline.SetTimeAutoScroll(x - m_Timeline.GetGap());
	else if (x > Width() - m_Timeline.GetGap())
		m_Timeline.SetTimeAutoScroll(x - Width() + m_Timeline.GetGap());
	else
		m_Timeline.SetTimeAutoScroll(0);
}

void LayerControl::HandlePaint(CDC * pDC, const CRect & rect)
{
	if (IsEmpty())
		return;

	CRect rClient = ClientRect();

	rClient.top = 1;
	ClipRect clip(this, pDC, rClient);

	CBrush bFace; bFace.CreateSysColorBrush(COLOR_BTNFACE);
	CBrush bLight; bLight.CreateSysColorBrush(COLOR_BTNHILIGHT);
	CBrush bHalfLight(My::MeanSysColor(COLOR_BTNFACE, COLOR_BTNHILIGHT));
	CBrush bHalfShadow(My::MeanSysColor(COLOR_BTNFACE, COLOR_BTNSHADOW));
	CBrush bShadow; bShadow.CreateSysColorBrush(COLOR_BTNSHADOW);

	if (GetLayer() != 0) {		

		rClient.bottom -= 1;

		Span s = GetSourceSpan();
		CRect rSourceSpan(rClient);
		rSourceSpan.left = m_Timeline.TimeToX(s.first); 
		rSourceSpan.right = m_Timeline.TimeToX(s.last) + 1;

		pDC->FillRect(&rSourceSpan, &bHalfLight);
		pDC->FrameRect(&rSourceSpan, &bShadow);

		CBrush bLight, bDark;
		if (GetLayer()->IsSelected()) {
			bLight.CreatePatternBrush(&m_LightGreenRipple);
			bDark.CreatePatternBrush(&m_DarkGreenRipple);
		} else {
			bLight.CreateSolidBrush(RGB(220, 246, 184));
			bDark.CreateSolidBrush(RGB(178, 232, 105));
		}
		
		CPoint org = pDC->SetBrushOrg(My::Control::GetWindowOffset() + 
			CPoint(m_Timeline.TimeToX(GetSourceOffset()), 0));

		s = GetTrimming().Offset(GetSourceOffset());
		CRect rSpan(rClient);
		rSpan.left = m_Timeline.TimeToX(s.first); 
		rSpan.right = m_Timeline.TimeToX(s.last) + 1;
		pDC->FillRect(&rSpan, &bLight);

		CBrush bFrame;
		bFrame.CreateSysColorBrush(COLOR_BTNSHADOW);
		pDC->FrameRect(&rSpan, &bFrame);

		m_LeftGlyph.Draw(pDC, rSpan.left - m_LeftGlyph.Width() + 1, rSpan.top);
		m_RightGlyph.Draw(pDC, rSpan.right - 1, rSpan.top);

		pDC->SetBrushOrg(org);
	}
}

void LayerControl::SetLayer(Layer * pLayer)
{
	if (pLayer != m_pLayer) {
		if (m_pLayer != 0) {
			m_pLayer->Detach(this);
			m_SourceOffset = -1;
			m_Trimming = Span(-1, -1);
		}
		m_pLayer = pLayer;
		if (m_pLayer != 0) {
			m_pLayer->Attach(this);
			m_SourceOffset = GetLayer()->GetSourceOffset();
			m_Trimming = GetLayer()->GetTrimming();
		}
		Invalidate();
	}
}

void LayerControl::ProcessEvent(Event & e)
{
	if (e.Is<Layer::TimeSpanChange>()) {
		SetSourceOffset(GetLayer()->GetSourceOffset());
	} 
	else if (e.Is<Layer::TrimmingChange>()) {
		SetTrimming(GetLayer()->GetTrimming());
	}
	else if (e.Is<Layer::StateChange>()) {
		Invalidate();
	}
	else
		Container::ProcessEvent(e);
}

// LayerRow

LayerRow::LayerRow(Xline & xline, const String & caption) :
	m_Xline(xline),
	m_Caption(caption),
	m_Control(* this),
	m_pLayer(0)
{
	Add(&m_Control);
//	Row * pRow1 = (GridRow *) this;
//	Row * pRow = this;
//	CRect bounds = pRow->Bounds();

	SetRowHeight(15);
}

LayerRow::~LayerRow()
{
	SetLayer(0);
}

bool LayerRow::IsEmpty() const
{
	return m_pLayer == 0 || m_pLayer->IsEmpty();
}

void LayerRow::SetLayer(Layer * pLayer)
{
	if (m_pLayer != pLayer) {
		if (m_pLayer != 0)
			m_pLayer->Detach(this);
		m_pLayer = pLayer;
		if (m_pLayer != 0)
			m_pLayer->Attach(this);
	}
	m_Control.SetLayer(pLayer);
	if (GetList() != 0)
		Invalidate();
}

void LayerRow::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	TreeRow::HandleLeftDown(flags, p);
	if (IsEmpty())
		return;
	CRect r(ClientRect());
	Span s = m_Xline.GetNameCol().GetBounds();
	r.left = s.first;
	r.right = s.last;
	GetApp().GetDoc().SetSelection(0);
	if (r.PtInRect(p))
		m_pLayer->Select(true);
}

void LayerRow::HandlePaint(CDC * pDC, const CRect & rClip)
{
	GridRow::HandlePaint(pDC, rClip);
	CRect rClient = ClientRect();
	CPen pen(PS_SOLID, 1, GetSysColor(COLOR_BTNHILIGHT));
	pDC->SelectObject(&pen);
	pDC->MoveTo(rClient.left, rClient.bottom - 1);
	pDC->LineTo(rClient.right, rClient.bottom - 1);
}

void LayerRow::PaintCell(CDC * pDC, const CRect & rClip, const Column & col, const CRect & rCell)
{
	if (&col == &m_Xline.GetNameCol()) {
		if (!IsEmpty() && m_pLayer->IsSelected()) {
			CRect r(rCell);
			r.right--;
			r.bottom--;
			CBrush bViolet(RGB(224, 224, 255));
			pDC->FillRect(&r, &bViolet);
		}
		COLORREF cText = RGB(0, 0, 0);
		if (IsEmpty())
			cText = My::MeanColor(GetSysColor(COLOR_BTNFACE), cText); 
		COLORREF cOld = pDC->SetTextColor(cText);
		TreeRow::PaintCaption(pDC, rCell);
		pDC->SetTextColor(cOld);
	}
}

void LayerRow::ProcessEvent(Event & e)
{
	if (e.Is<Layer::Event>()) {
		Invalidate();
	} 
	else if (e.Is<Doc::SelectionChange>()) {
		Invalidate();
	}
	GridRow::ProcessEvent(e);
}

} // My
